package jtech;
import static jtech.Game.getGraphicsDevice;
import static jtech.Game.getWindow;

import java.awt.Cursor;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.peer.RobotPeer;
import java.lang.reflect.Method;

public class Mouse implements MouseListener, MouseWheelListener, MouseMotionListener {
    
    private RobotPeer peer;

    private int currentPosX;
    private int currentPosY;
    private int windowCenterX;
    private int windowCenterY;
    
    private int mouseX, mouseY;

    private int wheelRotation;
    private int wheel;
    
    private boolean mouseVisible;
    private Cursor cursorEmpty;
    private Cursor cursorDefault;
    
    private boolean relativeMouseMode;
    
    private boolean[] currentState = new boolean[4];
    private boolean[] nextState = new boolean[4];
    
    public void init()  {
        
        try {
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Method method = toolkit.getClass().getMethod("createRobot", Robot.class, GraphicsDevice.class);
            peer = (RobotPeer) method.invoke(toolkit, null, getGraphicsDevice());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        
        if (!GraphicsEnvironment.isHeadless()) {
            final Toolkit toolkit = Toolkit.getDefaultToolkit();
            final Image emptyImage = toolkit.getImage("");
            cursorEmpty = toolkit.createCustomCursor(emptyImage, new Point(0, 0), "invisible");
            cursorDefault = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
        }
        
        setRelativeMouseMode(true);
        
        restart();
        
    }
    
    public void setCurrentPos(int x, int y) {
        currentPosX = x;
        currentPosY = y;
    }
    
    public void setWindowCenter(Point winfowCenter) {
        windowCenterX = winfowCenter.x;
        windowCenterY = winfowCenter.y;
    }
    
    public void setWheelRotation(int wheelRotation) {
        this.wheelRotation = wheelRotation;
    }

    public void shutdown() {
    
    }
    
    private void centralize() {
        currentPosX = windowCenterX;
        currentPosY = windowCenterY;
        peer.mouseMove(windowCenterX, windowCenterY);
    }
    
    public void restart() {
        mouseX = windowCenterX;
        mouseY = windowCenterY;
        centralize();
    }

    public void mouseMove() {
        
        if (getWindow().hasFocus() == false) {
            getWindow().setVisible(true);
            getWindow().requestFocus();
        }
        
        if (relativeMouseMode) {
            int mx = 0, my = 0;
            mx = currentPosX - windowCenterX;
            my = currentPosY - windowCenterY;
            
            mouseX += mx;
            mouseY += my;
            
            if (mx != 0 || my != 0) {
                centralize();
            }
        } else {
            mouseX = currentPosX;
            mouseY = currentPosY;
            // TODO: Implementar dx, dy
        }
        
        mouseX = Math.max(0, Math.min(getWindow().getWidth()-1, mouseX));
        mouseY = Math.max(0, Math.min(getWindow().getHeight()-1, mouseY));
        
    }
    
    public void wheelMove() {
        wheel += wheelRotation;
        wheelRotation = 0;
    }
    
    public void update() {
        for (int i = 0; i < currentState.length; i++) {
            currentState[i] = nextState[i];
        }
    }
    
    public boolean isRelativeMouseMode() {
        return relativeMouseMode;
    }

    public void setRelativeMouseMode(boolean relativeMouseMode) {
        this.relativeMouseMode = relativeMouseMode;
    }
    
    public void setMouseVisible(boolean visible) {
        if (!GraphicsEnvironment.isHeadless()) {
            if (visible) {
                getWindow().setCursor(cursorDefault);
            } else {
                getWindow().setCursor(cursorEmpty);
            }
            mouseVisible = visible;
        }
    }
    
    public boolean isMouseVisible() {
        return mouseVisible;
    }

    public int getMouseX() {
        return mouseX;
    }

    public int getMouseY() {
        return mouseY;
    }

    public int getWheel() {
        return wheel;
    }

    protected void setNextState(int button, boolean value) {
        nextState[button] = value;
    }

    public boolean isDown(int button) {
        return currentState[button];
    }

    public boolean isPressed(int button) {
        return !currentState[button] && nextState[button];
    }

    public boolean isReleased(int button) {
        return currentState[button] && !nextState[button];
    }

    protected void releaseAll() {
        for (int i = 0; i < nextState.length; i++) {
            nextState[i] = false;
        }
    }
    
 // MouseMotionListener
    @Override
    public void mouseDragged(MouseEvent e) {
        mouseMoved(e);
    }

    @Override
    public void mouseMoved(MouseEvent e) {
        int x = e.getX(); //e.getXOnScreen();
        int y = e.getY(); //e.getYOnScreen();
        setCurrentPos(x, y);
    }

    // MouseWheelListener
    @Override
    public void mouseWheelMoved(MouseWheelEvent e) {
        setWheelRotation(e.getWheelRotation());
    }

    // MouseListener
    @Override
    public void mouseClicked(MouseEvent e) {
    
    }

    @Override
    public void mousePressed(MouseEvent e) {
        setNextState(e.getButton(), true);
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        setNextState(e.getButton(), false);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        
    }

    @Override
    public void mouseExited(MouseEvent e) {
        
    }
    
}
